// File:       poscmd01.c++
// Version:    1.00
// Author:     (c) Miles Sabin, 1997
// Purpose:    insert string command

// Change log:
//  28/03/97   v. 1.00

#include "poscmds.h"

#include <math.h>        // for frexp()
#include <stdio.h>       // for sprintf()
#include "memory.h"
#include "ostream.h"
#include "streambuf.h"
#include "string.h"


// Implementation of InsertStringCommand

InsertStringCommand::InsertStringCommand(basic_ostream_char& os, char const* s, int n, int width)
  : s_(s),
    n_(n),
    width_(width)
  { execute_template(os); }

InsertStringCommand::~InsertStringCommand()
  {}

ios::iostate InsertStringCommand::execute(basic_ostream_char& os)
  {
    ios::iostate state = ios::goodbit;

    int adjust = (os.flags()&ios::adjustfield);
    char fill_char = os.fill();
    basic_streambuf_char* sb = os.rdbuf();

    if(adjust != ios::left && width_ > n_)
    {
      state = insert_fill(os, fill_char, width_-n_);
      width_ = n_;
    }

    if(state == ios::goodbit)
    {
      if(sb->sputn(s_, n_) != n_)
        state = ios::failbit;
      else if(width_ > n_)
        state = insert_fill(os, fill_char, width_-n_);
    }

    return state;
  }


// Implementation of basic_ostream_char

basic_ostream_char& basic_ostream_char::operator<<(void* p)
  {
    char ptr[16];
    int ptr_width = sprintf(ptr, "%p", p);
    return *this << ptr;
  }


// Implementation of basic_ostream_char

#ifdef BUILTIN_BOOL

basic_ostream_char& operator<<(bool n)
  {
    char* rep;

    if((flags()&boolalpha) == 0)
      rep = (n ? "1", : "0");
    else
      rep = (n ? "true" : "false");

    return *this << rep;
  }

#endif

#include "autodstry.c++"

inline void destroy(auto_ptr<char>* p)
{
  p->~auto_ptr();
};

basic_ostream_char& insert_floating(basic_ostream_char& os, double f)
{
  ios::fmtflags flags = os.flags();
  int precision = os.precision();
  char format[8];
  char* format_ptr = format;

  *format_ptr++ = '%';

  if((flags&ios::showpos) != 0)
    *format_ptr++ = '+';

  if((flags&ios::showpoint) != 0)
    *format_ptr++ = '#';

  if((flags&ios::fixed) != 0 || precision > 0)
  {
    *format_ptr++ = '.';
    *format_ptr++ = '*';
  }

  int buffer_size = 0;

  switch(flags&ios::floatfield)
  {
    case ios::fixed:
      {
        *format_ptr++ = 'f';
        int exp;
        frexp(f, &exp);
        buffer_size = (exp/3)+16;
        break;
      }

    case ios::scientific:
      {
        *format_ptr++ = ((flags&ios::uppercase) == 0 ? 'e' : 'E');
        buffer_size = precision+16;
        break;
      }

    default:
      {
        *format_ptr++ = ((flags&ios::uppercase) == 0 ? 'g' : 'G');
        int exp;
        frexp(f, &exp);
        buffer_size = ((exp < -4*4 || exp >= precision*4) ? precision+16 : (exp/3)+16);
        break;
      }
  }

  *format_ptr = 0;

  const int default_buffer_size = 32;
  char default_buffer[default_buffer_size];
  char* buffer;

  auto_ptr<char> auto_buffer;
  DESTROY_ON_THROW(auto_buffer);

  if(buffer_size < default_buffer_size)
    buffer = default_buffer;
  else
  {
    auto_buffer = auto_ptr<char>(reinterpret_cast(char*, ::operator new(buffer_size)));
    buffer = auto_buffer.get();
  }

  if((flags&ios::fixed) != 0 || precision > 0)
    sprintf(buffer, format, precision, f);
  else
    sprintf(buffer, format, f);

  return os << buffer;
}

basic_ostream_char& basic_ostream_char::operator<<(float f)
  {
    insert_floating(*this, f);
    return *this;
  }

basic_ostream_char& basic_ostream_char::operator<<(double f)
  {
    insert_floating(*this, f);
    return *this;
  }


// Implementation of basic_ostream_char free fns

basic_ostream_char& operator<<(basic_ostream_char& os, char c)
{
  InsertStringCommand cmd(os, &c, 1, os.width(0));
  return os;
}

basic_ostream_char& operator<<(basic_ostream_char& os, char const* s)
{
  InsertStringCommand cmd(os, s, basic_ostream_char::traits::length(s), os.width(0));
  return os;
}


// Implementation of basic_string_char free fns

basic_ostream_char& operator<<(basic_ostream_char& os, basic_string_char const& str)
{
  if(str.size() != 0)
    os << str.data();

  os.width(0);

  return os;
}
